import {
  BulgeCurveParam,
  EBomCurveType,
  EBomPlankGrooveType,
  EGrooveGeometryType,
  ETextureDirection,
  Groove,
  Hole,
  IPathGroove,
  Number3,
  Path,
  Point,
  Point3,
  Profile,
  Rect,
} from '@/types';
import {
  bulgeCurveParam2IntermediaCurveParam,
  createLineCurveParam,
  getContinuousCurveParams,
  getCurveFromBulge,
  getDirectoryByPlankFace,
  getFillCurveParamByCircle,
  getOffsetCurveParams,
  getPointQuadrant,
  getRoundedRectShape,
  groupPointsByQuadrant,
  intermediateCurveParam2BulgeCurveParam,
  number3ToPoint,
  object2Vector2,
  point2point3,
  point3point2,
  reverseCurveParams,
  xyz,
} from '@/utils';
import { Edges, Extrude, Line } from '@react-three/drei';
import { ConfigProvider, Table, TableColumnsType } from 'antd';
import classNames from 'classnames';
import {
  clone,
  cloneDeep,
  concat,
  flow,
  isEqual,
  isUndefined,
  map,
  merge,
  negate,
  round,
  uniqBy,
} from 'lodash-es';
import { ComponentProps, useState } from 'react';
import {
  EllipseCurve,
  Euler,
  Line3,
  LineCurve3,
  Matrix4,
  Quaternion,
  Shape,
  Vector3,
  Vector4,
} from 'three';
import { degToRad } from 'three/src/math/MathUtils';
import { Mark } from '../Mark';
import { config } from './edgeConfig';
import styles from './index.less';
import { baseColorConfig, colorConfig, ColorMap } from './colorConfig';
import { useBoolean } from 'ahooks';

interface HightLight {
  holes: string[];
  grooves: string[];
}

interface Props {
  profile: Profile;
  inners?: Profile[];
  width: number;
  height: number;
  holes: Hole[];
  grooves: Groove[];
  thickness: number;
  hightLight: HightLight;
  edgeBanding: number[];
  textureDirection: string;
  onHoleClick?: (holeId: string) => void;
  onHoleHover?: (holeId: string) => void;
  onGrooveClick?: (holeId: string) => void;
  onGrooveHover?: (holeId: string) => void;
  onPlankHover?: (hovered: boolean) => void;
}

export function Plank(props: Props) {
  const {
    profile,
    inners,
    width,
    height,
    thickness,
    holes,
    grooves,
    hightLight,
    edgeBanding,
    textureDirection,
    onHoleClick,
    onHoleHover,
    onGrooveClick,
    onGrooveHover,
    onPlankHover,
  } = props;

  return (
    <>
      <BasePlank
        {...{
          profile,
          inners,
          width,
          height,
          thickness,
          edgeBanding,
          onPlankHover,
        }}
      />
      {/* <PlankMark {...{ width, height, holes, grooves }} /> */}
      {holes.map((hole) => {
        return (
          <BaseHole
            onHoleClick={onHoleClick}
            onHoleHover={onHoleHover}
            key={hole.holeId}
            {...{ hole, hightLight }}
          />
        );
      })}
      {holes
        .filter((hole) => hightLight.holes.includes(hole.holeId))
        .map((hole) => {
          return (
            <TipArrow
              position={getMiddlePostionByStartAndEnd({
                startAndEnd: hole,
                offset: -thickness / 2,
              })}
            />
          );
        })}
      {grooves.map((groove) => {
        return (
          <BaseGroove
            key={groove.grooveId}
            onGrooveClick={onGrooveClick}
            onGrooveHover={onGrooveHover}
            {...{ thickness, groove, hightLight, width, height }}
          />
        );
      })}
      {grooves
        .filter((groove) => hightLight.grooves.includes(groove.grooveId))
        .map((groove) => {
          return (
            <TipArrow
              position={getMiddlePostionByStartAndEnd({
                startAndEnd: groove,
                offset: -thickness / 2,
              })}
            />
          );
        })}
      <BaseTextureDirection {...{ thickness, textureDirection }} />
    </>
  );
}

function ArrowLine(props: {
  startPoint: Point3;
  endPoint: Point3;
  color: string;
  lineWidth?: number;
}) {
  const { startPoint, endPoint, color, lineWidth = 1 } = props;
  const line3 = new Line3(new Vector3(...startPoint), new Vector3(...endPoint));
  const arrowLen = 10;
  const arrowAngle = degToRad(45);
  const arrowDirectionVector3 = new Vector3()
    .subVectors(line3.end, line3.start)
    .normalize()
    .negate();
  const axiosVector3 = new Vector3(0, 0, 1);
  const arrowPoint1Vector3 = arrowDirectionVector3
    .clone()
    .applyAxisAngle(axiosVector3, arrowAngle)
    .setLength(arrowLen);
  const arrowPoint2Vector3 = arrowDirectionVector3
    .clone()
    .applyAxisAngle(axiosVector3, -arrowAngle)
    .setLength(arrowLen);
  const arrowTopPointVector3 = new Vector3(0, 0, 0);

  return (
    <group>
      <Line
        points={[line3.start, line3.end]}
        color={color}
        lineWidth={lineWidth}
      />
      <Line
        position={line3.end}
        points={[arrowPoint1Vector3, arrowTopPointVector3, arrowPoint2Vector3]}
        color={color}
        lineWidth={lineWidth}
      />
    </group>
  );
}

function BaseTextureDirection(props: {
  thickness: number;
  textureDirection: string;
}) {
  const { thickness, textureDirection } = props;
  const z = thickness / 2;
  const startPoint: Point3 = [0, 0, z];
  const endPoint: Point3 = [100, 0, z];
  const color = 'red';
  if (!textureDirection || ETextureDirection.None === textureDirection) {
    return null;
  }
  const rotation = new Euler(
    0,
    0,
    ETextureDirection.Horizontal === textureDirection ? 0 : degToRad(90),
  );

  return (
    <group rotation={rotation}>
      <ArrowLine {...{ startPoint, endPoint, color }} />
      <group rotation={new Euler(0, 0, degToRad(180))}>
        <ArrowLine {...{ startPoint, endPoint, color }} />
      </group>
    </group>
  );
}

function getEdgeColorConfig(edgeBanding: number[]) {
  return edgeBanding.map((thickness) => {
    const target = config.edgeBanding.find((item) => {
      return item.thickness.includes(thickness);
    });
    const color = target?.color ?? '';
    return { thickness, color };
  });
}

const columns: TableColumnsType<any> = [
  {
    title: '封边厚度',
    dataIndex: 'thickness',
    align: 'center',
    render: (text: string) => <>{text}&nbsp;mm</>,
  },
  {
    title: '场景示意',
    dataIndex: 'color',
    align: 'center',
    render: (text: string) => {
      return (
        <div
          className={classNames(styles.indicator, 'm-auto')}
          style={{ borderColor: text }}
        ></div>
      );
    },
  },
];

const stripeClassName = classNames('bg-gray-50');
const isEven = (i: number) => i % 2 === 0;
const stripeRowClassName = (_: unknown, i: number) =>
  isEven(i + 1) ? stripeClassName : '';
export function PlankEdgeLegend(props: { edgeBanding: number[] }) {
  const { edgeBanding } = props;

  const colorConfig = getEdgeColorConfig(edgeBanding);
  // console.log(colorConfig);
  const legends = uniqBy(colorConfig, 'thickness');

  return (
    <div className={classNames(styles.legend, 'w-full')}>
      <Table
        rowKey={'thickness'}
        rowClassName={stripeRowClassName}
        pagination={false}
        scroll={{ y: 100 }}
        className="w-full"
        columns={columns}
        dataSource={legends}
      />
    </div>
  );
}

function PlankEdge(props: {
  shape: Shape;
  thickness: number;
  colors?: string[];
  edgeBanding: number[];
}) {
  const {
    shape,
    thickness,
    colors = ['red', 'green', 'blue', 'purple', 'black'],
    edgeBanding,
  } = props;
  const cloneShape = shape.clone();
  cloneShape.closePath();
  // console.log({ curves: cloneShape.curves });

  const colorConfig = getEdgeColorConfig(edgeBanding);
  const newColors: string[] = map(colorConfig, 'color');
  const colorNum = newColors.length;
  // console.log({ newColors });

  return (
    <group>
      {cloneShape.curves.map((curve, i) => {
        const color = newColors[i % colorNum];
        const shape = new Shape();
        shape.add(curve);
        if ((curve as EllipseCurve).isEllipseCurve) {
          const ellipseCurve = curve.clone() as EllipseCurve;
          const { aStartAngle, aEndAngle, aClockwise } = ellipseCurve;
          ellipseCurve.aStartAngle = aEndAngle;
          ellipseCurve.aEndAngle = aStartAngle;
          ellipseCurve.aClockwise = !aClockwise;
          shape.add(ellipseCurve);
        }

        return (
          <group key={shape.uuid}>
            <Extrude
              args={[
                shape,
                {
                  depth: thickness,
                },
              ]}
            >
              <meshStandardMaterial args={[{ color }]} />
            </Extrude>
          </group>
        );
      })}
    </group>
  );
}

function BasePlank(props: {
  profile: Profile;
  inners?: Profile[];
  width: number;
  height: number;
  thickness: number;
  edgeBanding: number[];
  onPlankHover?: (hovered: boolean) => void;
}) {
  const {
    profile,
    inners,
    width,
    height,
    thickness,
    edgeBanding,
    onPlankHover,
  } = props;
  const shape = getPlankShape({ profile, inners, width, height });
  const [isHover, { set: setHover }] = useBoolean();

  return (
    <group
      onPointerOver={() => {
        onPlankHover?.(true);
        setHover(true);
      }}
      onPointerOut={() => {
        onPlankHover?.(false);
        setHover(false);
      }}
      position={[0, 0, -thickness / 2]}
      renderOrder={1}
    >
      <Extrude
        args={[
          shape,
          {
            depth: thickness,
            // extrudePath: new LineCurve3(new Vector3(0, 0, - thickness / 2), new Vector3(0, 0, thickness / 2))
          },
        ]}
      >
        <meshStandardMaterial
          args={[
            {
              color: baseColorConfig.plank,
              transparent: true,
              opacity: 0,
            },
          ]}
        />
        <Edges key={shape.uuid} scale={1} color="black" />
      </Extrude>
      {edgeBanding.length === 0 ? null : (
        <PlankEdge {...{ shape, thickness, edgeBanding }} />
      )}
    </group>
  );
}

function BaseHole(props: {
  hole: Hole;
  hightLight: HightLight;
  onHoleClick?: (holeId: string) => void;
  onHoleHover?: (holeId: string) => void;
}) {
  const { hole, hightLight, onHoleClick, onHoleHover } = props;
  const { holeId } = hole;
  const [isHover, { set: setHover }] = useBoolean();
  const isSelected = hightLight.holes.includes(holeId);

  return (
    <Extrude
      onPointerOver={(e) => {
        e.stopPropagation();
        // console.log('onHoleHover', true, 'holeId', holeId);
        onHoleHover?.(holeId);
        setHover(true);
      }}
      onPointerOut={(e) => {
        e.stopPropagation();
        // console.log('onHoleHover', false, 'holeId', holeId);
        onHoleHover?.('');
        setHover(false);
      }}
      onClick={(e) => {
        e.stopPropagation();
        onHoleClick?.(holeId);
      }}
      args={[
        getHoleShape(hole),
        {
          depth: hole.depth,
          extrudePath: getExtrudePathByStartAndEnd(hole),
        },
      ]}
    >
      <meshStandardMaterial
        args={[
          {
            color: findColor(
              colorConfig.hole[
                hightLight.holes.includes(hole.holeId) ? 'hightLight' : 'normal'
              ],
              hole.plankFaceId,
            ),
            transparent: true,
            opacity: isHover
              ? baseColorConfig.hover.opacity
              : baseColorConfig.opacity,
          },
        ]}
      />
      <Edges scale={1} color="gray" />
    </Extrude>
  );
}
function TipArrow(props: { position: Point3 }) {
  const { position } = props;
  const config = {
    radius: 40,
    height: 60,
    color: 'red',
  };
  const arrowDirectionVector3 = new Vector3(0, 0, -1);
  const arrowStartVector3 = new Vector3().fromArray(position);
  const arrowEndVector3 = arrowStartVector3
    .clone()
    .addScaledVector(arrowDirectionVector3.clone().negate(), config.height);

  return (
    <arrowHelper
      args={[
        arrowDirectionVector3,
        arrowEndVector3,
        config.height,
        config.color,
      ]}
    />
  );
}

function getMiddlePostionByStartAndEnd(params: {
  startAndEnd: {
    start: Number3;
    end: Number3;
  };
  offset?: number;
}) {
  const { startAndEnd, offset } = params;
  const config = {
    radius: 40,
    height: 60,
    color: 'red',
  };
  const [startVector3, endVector3] = [startAndEnd.start, startAndEnd.end].map(
    (p) => new Vector3().fromArray(xyz(p)),
  );
  const middleVector3 = new Line3(startVector3, endVector3).getCenter(
    new Vector3(),
  );
  const arrowStartVector3 = middleVector3;
  arrowStartVector3.setZ(0);
  const arrowDirectionVector3 = new Vector3(0, 0, -1);
  const arrowEndVector3 = arrowStartVector3
    .clone()
    .addScaledVector(arrowDirectionVector3.clone().negate(), config.height);

  const position = middleVector3.add(new Vector3(0, 0, offset)).toArray();
  return position;
}

function BaseGroove(props: {
  width: number;
  height: number;
  thickness: number;
  groove: Groove;
  hightLight: HightLight;
  onGrooveClick?: (holeId: string) => void;
  onGrooveHover?: (holeId: string) => void;
}) {
  const { groove, hightLight } = props;

  const { shape, translation, euler } = getGrooveShapeAndMatrix(groove, {
    width: props.width,
    height: props.height,
    thickness: props.thickness,
  });

  const lineColor = 'blue';

  return (
    <>
      <Extrude
        onClick={(e) => {
          e.stopPropagation();
          props.onGrooveClick?.(groove.grooveId);
        }}
        onPointerOver={(e) => {
          e.stopPropagation();
          props.onGrooveHover?.(groove.grooveId);
        }}
        onPointerOut={(e) => {
          e.stopPropagation();
          props.onGrooveHover?.('');
        }}
        rotation={euler}
        position={translation}
        args={[
          shape,
          {
            depth: -groove.depth,
          },
        ]}
      >
        <meshStandardMaterial
          args={[
            {
              depthWrite: false,
              color: findColor(
                colorConfig.groove[
                  hightLight.grooves.includes(groove.grooveId)
                    ? 'hightLight'
                    : 'normal'
                ],
                groove.plankFaceId,
              ),
              transparent: true,
              opacity: 0.5,
            },
          ]}
        />
        <Edges scale={1} color="gray" />
      </Extrude>
      {groove.start &&
        (groove.type === EBomPlankGrooveType.LAMELLO ? (
          <ArrowLine
            startPoint={xyz(groove.start)}
            endPoint={xyz(groove.end)}
            color={'lightgreen'}
            lineWidth={2}
          />
        ) : (
          <Line
            points={[groove.start, groove.end].map(xyz)}
            color={lineColor}
            lineWidth={1}
            dashed={false}
          />
        ))}
    </>
  );
}

function PlankMark(props: {
  width: number;
  height: number;
  holes: Hole[];
  grooves: Groove[];
}) {
  type MarkProps = ComponentProps<typeof Mark>;
  type Tick = NonNullable<MarkProps['ticks']>[0];
  type StartPoint = Pick<Hole, 'start'>;

  const { width, height, holes, grooves } = props;
  const points = concat<StartPoint>(holes, grooves)
    .filter((hole) => hole.start)
    .map((hole) => point3point2(xyz(hole.start)));
  const groupedByQuadrantPoints = groupPointsByQuadrant(points);

  return (
    <>
      {getLinesFromReact({ width, height })
        .map((line) => line.map(point2point3))
        .map(([start, end]) => {
          const markQuadrants = [start, end].map((point) => {
            return getPointQuadrant(point3point2(point));
          });

          const markPoints = markQuadrants
            .map((markQuadrant) => {
              return groupedByQuadrantPoints[markQuadrant];
            })
            .filter(negate(isUndefined))
            .flat();

          const point3s = markPoints.map(point2point3);

          const [startVector3, endVector3] = [start, end].map((p) => {
            return new Vector3(...p);
          });
          const line3 = new Line3(startVector3, endVector3);
          const lineLen = line3.distance();
          const lineLenHalf = lineLen / 2;

          const lineMiddlePointVector3 = line3.getCenter(new Vector3());
          point3s.push(lineMiddlePointVector3.toArray());

          const ticks = point3s.map<Tick>((point3) => {
            const holeStartVector3 = new Vector3(...point3);
            const pointInlineVector3 = new Vector3();
            line3.closestPointToPoint(
              holeStartVector3,
              true,
              pointInlineVector3,
            );

            const position = pointInlineVector3.toArray();

            const distanceToStart = pointInlineVector3.distanceTo(startVector3);

            let edgeDistance =
              distanceToStart < lineLenHalf
                ? distanceToStart
                : lineLen - distanceToStart;

            let direction: Tick['direction'];
            if (distanceToStart < lineLenHalf) {
              direction = 'start';
            } else if (distanceToStart > lineLenHalf) {
              direction = 'end';
            }

            const text = round(edgeDistance).toFixed();
            return { position, text, direction };
          });

          return (
            <Mark
              key={`${start.toString()}-${end.toString()}`}
              {...{ start, end, ticks }}
            />
          );
        })}
    </>
  );
}

function getPointsFromRect(rect: Rect) {
  const { width, height } = rect;
  let points: Point[];
  const offset = (x: number, y: number): Point => [
    -width / 2 + x,
    -height / 2 + y,
  ];
  points = [
    [0, 0],
    [0, height],
    [width, height],
    [width, 0],
    // [0, 0],
  ];
  points = points.map(([x, y]) => offset(x, y));
  return points;
}

function getLinesFromReact(rect: Rect) {
  const points = getPointsFromRect(rect);

  const lines: [Point, Point][] = [];
  const pointNum = points.length;
  for (let index = 0; index < points.length; index++) {
    const point = points[index];
    let nextIndex = index + 1;
    if (nextIndex >= pointNum) {
      nextIndex = 0;
    }
    const nextPoint = points[nextIndex];
    lines.push([point, nextPoint]);
  }
  return lines;
}

function getShapeFromPoints(points: Point[]) {
  const shape = new Shape();
  const [startPoint, ...otherPoints] = points;
  {
    const [x, y] = startPoint;
    shape.moveTo(x, y);
  }
  otherPoints.map(([x, y]) => {
    shape.lineTo(x, y);
  });
  return shape;
}

const getRectShape = flow(getPointsFromRect, getShapeFromPoints);

function getClosedProfile(profile: Profile) {
  const { points, curves } = profile;
  const firstPoint = points.slice(0, 2);
  const lastPoint = points.slice(-2);
  if (isEqual(firstPoint, lastPoint)) return profile;
  const newPoints = clone(points).concat(firstPoint);
  const newCurves = clone(curves).concat({
    type: EBomCurveType.LineSeg,
    bulge: 0,
  });
  const newProfile: Profile = merge(cloneDeep(profile), {
    points: newPoints,
    curves: newCurves,
  });
  return newProfile;
}

function getCurveParamsFromProfile(profile: Profile) {
  const { points, curves } = getClosedProfile(profile);
  return curves.map((curve, i) => {
    const { bulge = 0 } = curve;
    const startPoint = { x: points[2 * i], y: points[2 * i + 1] };
    const endPoint = {
      x: points[2 * ((i + 1) % curves.length)],
      y: points[2 * ((i + 1) % curves.length) + 1],
    };
    return { startPoint, endPoint, bulge };
  });
}
function getCurveParamsFromPath(path: Path): BulgeCurveParam[] {
  const { points, curves } = path;
  const curveParams = curves.map((curve, i) => {
    const { bulge = 0 } = curve;
    const startPoint = { x: points[2 * i], y: points[2 * i + 1] };
    const endPoint = {
      x: points[2 * (i + 1)],
      y: points[2 * (i + 1) + 1],
    };
    return { startPoint, endPoint, bulge };
  });
  return curveParams;
}

function getShapeFromPath({
  path,
  width,
}: {
  path: Path;
  width: number;
}): Shape {
  const isClosed = path.points.length === path.curves.length * 2;
  let pathCurveParams = isClosed
    ? getCurveParamsFromProfile(path)
    : getCurveParamsFromPath(path);
  pathCurveParams = pathCurveParams.filter(
    (item) =>
      !object2Vector2(item.startPoint).equals(object2Vector2(item.endPoint)),
  );

  const offsetCurveParamsGroup: {
    outer: BulgeCurveParam[];
    inner: BulgeCurveParam[];
  } = { outer: [], inner: [] };
  map(pathCurveParams, (curveParam) => {
    const { outerCurveParam, innerCurveParam } = getOffsetCurveParams(
      curveParam,
      width,
    );
    offsetCurveParamsGroup.outer.push(outerCurveParam);
    offsetCurveParamsGroup.inner.push(innerCurveParam);
  });

  const [outerCurveParams, innerCurveParams] = [
    {
      offsetCurveParams: offsetCurveParamsGroup.outer,
      isCCW: false,
    },
    {
      offsetCurveParams: offsetCurveParamsGroup.inner,
      isCCW: true,
    },
  ].map(({ offsetCurveParams, isCCW }) =>
    flow(
      ({
        offsetCurveParams,
        isCCW,
      }: {
        offsetCurveParams: BulgeCurveParam[];
        isCCW: boolean;
      }) => ({
        intermediateOffsetCurveParams: offsetCurveParams.map(
          bulgeCurveParam2IntermediaCurveParam,
        ),
        isCCW,
      }),
      ({ intermediateOffsetCurveParams, isCCW }) =>
        getContinuousCurveParams(
          intermediateOffsetCurveParams,
          width,
          (pairCurveParam, index) => {
            const center = pathCurveParams[index].endPoint;
            const radius = width;
            const circle = { center, radius };
            return getFillCurveParamByCircle(pairCurveParam, circle, isCCW);
          },
        ),
    )({ offsetCurveParams, isCCW }),
  );

  const linkCurveParamsGroup = {
    start: createLineCurveParam({
      startPoint: outerCurveParams.at(-1)!.endPoint,
      endPoint: innerCurveParams.at(-1)!.endPoint,
    }),
    end: createLineCurveParam({
      startPoint: innerCurveParams[0].startPoint,
      endPoint: outerCurveParams[0].startPoint,
    }),
  };
  const composeCurveParams = [
    ...outerCurveParams,
    linkCurveParamsGroup.start,
    ...reverseCurveParams(innerCurveParams),
    linkCurveParamsGroup.end,
  ];

  const curves = composeCurveParams.map(
    flow(intermediateCurveParam2BulgeCurveParam, getCurveFromBulge),
  );
  const shape = new Shape();
  curves.forEach(shape.add.bind(shape));

  // console.log({
  //   outerCurveParams,
  //   innerCurveParams,
  //   composeCurveParams,
  //   composeCurveParamsPointsStr: getPointsStrFromStartEnd(composeCurveParams),
  //   shape,
  // });

  return shape;
}
function getShapeFromProfile(profile: Profile) {
  const curveParams = getCurveParamsFromProfile(profile);
  return getShapeFromCurveParam(curveParams);
}
function getShapeFromCurveParam(curveParams: BulgeCurveParam[]) {
  const shape = new Shape();
  const curves = curveParams.map(getCurveFromBulge);
  curves.forEach(shape.add.bind(shape));
  return shape;
}
function getPlankShape({
  profile,
  inners,
  width,
  height,
}: {
  profile: Profile;
  inners?: Profile[];
  width: number;
  height: number;
}) {
  let shape: Shape;
  if (!profile) {
    shape = getRectShape({ width, height });
  } else {
    shape = getShapeFromProfile(profile);
  }
  if (inners) {
    shape.holes = inners.map((profile) => getShapeFromProfile(profile));
  }
  return shape;
}

function getHoleShape(hole: Hole) {
  const { diameter } = hole;

  const shape = new Shape();
  shape.absarc(0, 0, diameter / 2, 0, 2 * Math.PI, true);

  return shape;
}

function getGrooveShapeAndMatrix(
  groove: Groove,
  plank: { width: number; height: number; thickness: number },
) {
  const { width, depth, geometryType, plankFaceId } = groove;

  let shape = new Shape();
  let matrix = new Matrix4();
  // 顶面
  if (Number(plankFaceId) === 0) {
    matrix.makeTranslation(0, 0, plank.thickness / 2);
  }
  if (Number(plankFaceId) === 5) {
    matrix.makeTranslation(0, 0, -(plank.thickness / 2 - depth));
  }

  switch (geometryType) {
    case EGrooveGeometryType.ROUND_CORNER: {
      const { radius } = groove;
      const line = new Line3(
        new Vector3(groove.start.x, groove.start.y, groove.start.z),
        new Vector3(groove.end.x, groove.end.y, groove.end.z),
      );
      const w = width!;
      const h = line.distance();
      const r = radius!;
      shape = getRoundedRectShape(new Shape(), -w / 2, -h / 2, w, h, r);
      // 从当前顶点转到x,y平面
      matrix = getGrooveMatrix(groove.plankFaceId, [
        number3ToPoint(groove.start),
        number3ToPoint(groove.end),
      ]);
      const direction = getDirectoryByPlankFace(Number(plankFaceId), Vector3);
      matrix.premultiply(
        new Matrix4().makeRotationFromQuaternion(
          new Quaternion().setFromUnitVectors(
            new Vector3()
              .subVectors(
                line.start.applyMatrix4(matrix),
                line.end.applyMatrix4(matrix),
              )
              .normalize(),
            new Vector3(0, 1, 0),
          ),
        ),
      );
      matrix.invert();
      // 以槽的起点和和终点所在的面作为挤压几何的终点
      matrix.premultiply(
        new Matrix4().makeTranslation(
          direction.clone().multiplyScalar(groove.depth),
        ),
      );
      break;
    }

    case EGrooveGeometryType.SPECIAL_SHAPED: {
      const { profile } = groove;
      shape = getShapeFromProfile(profile);
      break;
    }

    case EGrooveGeometryType.PATH: {
      const pathGroove = groove as IPathGroove;
      shape = getShapeFromPath(pathGroove);
      break;
    }

    case EGrooveGeometryType.SQUARE:
    default: {
      // 从当前顶点转到x,y平面
      matrix = getGrooveMatrix(groove.plankFaceId, [
        number3ToPoint(groove.start),
        number3ToPoint(groove.end),
      ]);
      const direction = getDirectoryByPlankFace(Number(plankFaceId), Vector3);
      // 将点转换成x, y水平面上的点
      const points = getSquareGroovePoints(groove, matrix);
      matrix.invert();
      // 以槽的起点和和终点所在的面作为挤压几何的终点
      matrix.premultiply(
        new Matrix4().makeTranslation(
          direction.clone().multiplyScalar(groove.depth),
        ),
      );
      shape = getShapeFromPoints(points.map(point3point2));
    }
  }

  const translation = new Vector3();
  const quaternion = new Quaternion();
  matrix.decompose(translation, quaternion, new Vector3());
  const euler = new Euler().setFromQuaternion(quaternion);

  return { shape, matrix, translation, euler };
}

function getGrooveMatrix(
  plankFaceId: string | number,
  points: Point3[],
): Matrix4 {
  const direction = getDirectoryByPlankFace(Number(plankFaceId), Vector3);

  const centerPoint = points
    .map((point) => {
      return new Vector3().fromArray(point);
    })
    .reduce((pre, cur) => {
      return pre.add(cur);
    })
    .multiplyScalar(1 / points.length);

  const matrix = new Matrix4()
    .makeTranslation(-centerPoint.x, -centerPoint.y, -centerPoint.z)
    .premultiply(
      new Matrix4().makeRotationFromQuaternion(
        new Quaternion().setFromUnitVectors(direction, new Vector3(0, 0, 1)),
      ),
    );

  return matrix;
}
function getSquareGroovePoints(
  { start, end, width, plankFaceId }: Groove,
  matrix: Matrix4,
): Point3[] {
  const startVec = new Vector3(start.x, start.y, start.z);
  const endVec = new Vector3(end.x, end.y, end.z);
  const line = new Line3(startVec, endVec);
  const lineDirection = line.delta(new Vector3());
  const planNormal = getDirectoryByPlankFace(Number(plankFaceId), Vector3);

  const pointOffsetDiretion = lineDirection
    .cross(planNormal)
    .normalize()
    .setLength(width! / 2);

  return [
    startVec.clone().add(pointOffsetDiretion),
    endVec.clone().add(pointOffsetDiretion),
    endVec.clone().sub(pointOffsetDiretion),
    startVec.clone().sub(pointOffsetDiretion),
  ].map((vec) => vec.applyMatrix4(matrix).toArray());
}

function getExtrudePathByStartAndEnd(points: { start: Number3; end: Number3 }) {
  const { start, end } = points;
  const p1 = new Vector3(start.x, start.y, start.z); //顶点1坐标
  const p2 = new Vector3(end.x, end.y, end.z); //顶点2坐标
  // 三维直线LineCurve3
  const LineCurve = new LineCurve3(p1, p2);

  return LineCurve;
}

function findColor(colorMap: ColorMap, plankFaceId: number) {
  return colorMap[plankFaceId] ?? colorMap.other;
}
